package com.piper.spring.aop;

import com.piper.spring.servlet.DispatchServlet;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
 * 方法拦截器
 */
public class MyMethodInterceptor implements MethodInterceptor {

    /**
     * 单例模式
     */
    private MyMethodInterceptor() {
    }

    private static class MethodInterceptorHolder {
        private static MyMethodInterceptor instance = new MyMethodInterceptor();
    }

    public static MyMethodInterceptor getInstance() {
        return MethodInterceptorHolder.instance;
    }


    /**
     * 创建代理类
     *
     * @param clz 目标对象
     * @return 代理对象
     */
    public static Object getMethodProxy(Class<?> clz) {
        return Enhancer.create(clz, MethodInterceptorHolder.instance);
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //判断当前类是否在aop容器中存在
        //如果存在 在方法前后执行 前置和后置逻辑
        Map<String, TreeSet<AopConfig>> aopContainer = DispatchServlet.getAopContainer();
        String name = o.getClass().getName();
        name = name.substring(0, name.indexOf("$$"));

        String key = name + "." + method.getName();
        Set<AopConfig> treeSet = aopContainer.get(key);

        //前置逻辑
        if (null != treeSet) {
            int size = treeSet.size();
            AopConfig[] configs = treeSet.toArray(new AopConfig[size]);
            for (int i = size - 1; i >= 0; i--) {
                configs[i].before(o, method, objects);
            }
        }

        //调用代理方法
        Object invoke = methodProxy.invokeSuper(o, objects);

        //后置逻辑
        if (null != treeSet) {
            int size = treeSet.size();
            AopConfig[] configs = treeSet.toArray(new AopConfig[size]);
            for (int i = 0; i < size; i++) {
                configs[i].after(o, method, objects);
            }
        }

        return invoke;
    }

}
